package com.yonyou.cyxdms.sysmanage.client.interceptor;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.yonyou.cyx.framework.bean.dto.base.ClientInfoDTO;
import com.yonyou.cyx.framework.compent.redis.RedisClient;
import com.yonyou.cyx.framework.util.bean.ApplicationContextHelper;
import com.yonyou.cyx.function.exception.ServiceBizException;
import com.yonyou.cyx.function.utils.bean.BeanMapperUtil;
import com.yonyou.cyx.function.utils.common.StringUtils;
import com.yonyou.cyx.function.utils.cookie.CookieUtil;
import com.yonyou.cyx.function.utils.jsonserializer.JSONUtil;
import com.yonyou.cyxdms.framework.bean.dto.user_info.LoginInfoDto;
import com.yonyou.cyxdms.framework.exception.DMSFrameworkException;
import com.yonyou.cyxdms.sysmanage.client.constants.BaseDataCommonConstants;
import com.yonyou.cyxdms.sysmanage.client.domains.dto.AppVersionDto;
import com.yonyou.cyxdms.sysmanage.client.domains.dto.MenuActionUrlDto;
import com.yonyou.cyxdms.sysmanage.client.feign.SysmanageDataCommonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 对DMS 框架进行常规拦截
 *
 * @author zhangxc
 * @date 2016年12月10日
 */

public class DMSSysManageInterceptor extends HandlerInterceptorAdapter {
    // 定义日志接口
    private static final Logger logger = LoggerFactory.getLogger(DMSSysManageInterceptor.class);

    @Autowired
    SysmanageDataCommonClient sysmanageDataCommonClient;
    @Autowired
    RedisClient redisClient;

    /**
     * Spring 的拦截器接口
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception (non-Javadoc)
     * @author maxingjian
     * @date 2019年08月13日
     * @see HandlerInterceptorAdapter#preHandle(HttpServletRequest, HttpServletResponse, Object)
     */
    @Autowired
    private Environment env;

    public String getMainAppId() {
        return env.getProperty("mainAppId");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        HttpServletRequest httpServletRequest = request;
        //获取请求的URL
        String path = httpServletRequest.getRequestURI();
        logger.debug("into LoginFilter:请求的地址:{}", path);
        logger.debug("请求方法:{}", httpServletRequest.getMethod());

        if (isIntercept(path)) {
            return true;
        }
        Map<String, String> mapVersions = checkAppVersion(request);
        logger.info("校验checkAppVersion：{}", mapVersions);
        if (!ObjectUtils.isEmpty(mapVersions)) {
            throw new DMSFrameworkException(999, JSONUtil.objectToJson(mapVersions), null);
        }

        //判断是否登陆
        LoginInfoDto loginInfo = ApplicationContextHelper.getBeanByType(LoginInfoDto.class);
        ClientInfoDTO clientInfoDTO = ApplicationContextHelper.getBeanByType(ClientInfoDTO.class);
        String userId = request.getHeader("userId");
        String userAgent = request.getHeader("userAgent");
        String appId = request.getHeader("appId");
        String authorization = request.getHeader("Authorization");
        logger.debug("[本次用户userId,userAgent]:{}，{}", userId, userAgent);
        loginInfo.setAppId(appId);
        if (StringUtils.isNullOrEmpty(appId)) {
            appId = "";
        }
        if(!StringUtils.isNullOrEmpty(authorization)&&initDmsLoginInfoDto(authorization)){
            logger.info("[店端账号登录!]");
            return true;
        }
        if (StringUtils.isNullOrEmpty(userId)) {
            logger.debug("[本次没有userID，拦截器结束了！]");
            return true;
        }
        if (StringUtils.isNullOrEmpty(userAgent) || "pc".equals(userAgent) || "10041001".equals(userAgent)) {
            userAgent = "10041001";
        } else {
            userAgent = "10041002";
        }
        logger.debug("[处理后的userAgent]:{}", userAgent);
        boolean isSubSystem = !(StringUtils.isNullOrEmpty(getMainAppId())     // 若未配置此参数，则按照主系统处理
                || "undefined".equals(appId)
                || (getMainAppId().contains(appId)));           // 判断传过来的appId是否不是主系统appId
        logger.debug("mainAppId:{}，appId:{}，是否为子系统：{}", getMainAppId(), appId, isSubSystem);
        LoginInfoDto loginInfos = getLoginInfo(userAgent);
        logger.debug("从redis或者数据库中获取的LoginInfoDTO:{}", JSONUtil.objectToJson(loginInfos));
        // 若未能取得LoginInfoDTO，则信任网关传递下来的header中的数据
        if (loginInfos == null
                || ((StringUtils.isNullOrEmpty(loginInfos.getAppId()) || "null".equals(loginInfos.getAppId()))
                && StringUtils.isNullOrEmpty(loginInfos.getUserId()))) {
            loginInfos = getLoginInfoDto(request);
            logger.debug("---------loginInfos为空时从header中获取-----------");
            logger.debug(new Gson().toJson(loginInfos));
        }
        BeanMapperUtil.copyProperties(loginInfos, loginInfo);
        BeanMapperUtil.copyProperties(loginInfos, clientInfoDTO);

        /**
         * APP认证校验
         * 如果PP认证校验没有jwt信息，不提醒用户未登录
         */
        if (path.endsWith("/appAuthCas")) {
            logger.debug("appAuthCas 放行:{}", loginInfo);
            return true;
        }

        //
        if(!StringUtils.isNullOrEmpty(authorization) && !checkUserMenuAndUrlMatch(path, httpServletRequest.getMethod(), loginInfo.getUserOrgId())){
            throw new ServiceBizException("当前用户没有此请求权限");
        }

        //获得token 的值
        String requestLocale;
        if ((requestLocale = CookieUtil.getValueFromCookie(httpServletRequest, "language")) != null) {
            loginInfo.setLocale(new Locale(requestLocale));
        }
        logger.debug("out DMSFrameworkInterceptor sucess");
        
        //2020-03-15   屏蔽日志记录功能
        try {
            String userLoginId = request.getHeader("userLoginId");
            if (!StringUtils.isNullOrEmpty(userLoginId)) {
                String format = MessageFormat.format(BaseDataCommonConstants.USER_LOGIN, userLoginId);
                Object o = redisClient.get(format);
                if (ObjectUtils.isEmpty(o)) {
                    sysmanageDataCommonClient.upLog(Long.parseLong(userLoginId), null, 10041002);
                }
            }
        } catch (Exception e) {
            logger.error("日志记录失败", e);
        }
        return true;
    }

    /**
     * 对请求URL进行判断
     * @param url
     * @param method
     * @param userOrgId
     * @return false则该用户无此请求权限
     */
    public Boolean checkUserMenuAndUrlMatch(String url, String method, Long userOrgId){
        logger.info("URL PATH:{}", url);
        logger.info("URL METHOD:{}", method);
        logger.info("UserOrgId:{}", userOrgId);
        AntPathMatcher pathMatcher = new AntPathMatcher();
        String appId = "RL";
        String userMenuCacheKey = MessageFormat.format(BaseDataCommonConstants.USER_MENU_CACHE, userOrgId);
        List menuIds = redisClient.getListAll(userMenuCacheKey);
        if(CollectionUtils.isEmpty(menuIds)){
            return true;
        }
        List<Long> userMenuIds = new ArrayList<>();
        menuIds.forEach(t -> userMenuIds.add(Long.valueOf(t.toString())));
        logger.info("USER MENU IDS:{}", JSON.toJSONString(userMenuIds));
        String redisActionUrlKey = MessageFormat.format(BaseDataCommonConstants.MENU_URL_ACTION_CACHE, "RL");
        List menuAction = redisClient.getListAll(redisActionUrlKey);
        if(CollectionUtils.isEmpty(menuAction)){
            return true;
        }
        List<MenuActionUrlDto> menuActionAll = JSON.parseArray(JSON.toJSONString(menuAction), MenuActionUrlDto.class);
        List<MenuActionUrlDto> matchActions = menuActionAll.stream().filter(t -> method.equals(t.getActionMethod()) &&
                pathMatcher.match(t.getActionUrl(), url)).collect(Collectors.toList());
        logger.info("Match Actions:{}", JSON.toJSONString(matchActions));
        if(CollectionUtils.isEmpty(matchActions)){
            return true;
        }
        List<Long> matchMenuIds = matchActions.stream().map(t -> t.getMenuId()).collect(Collectors.toList());
        logger.info("matchMenuIds :{}", JSON.toJSONString(matchMenuIds));
        userMenuIds.retainAll(matchMenuIds);
        return CollectionUtils.isEmpty(userMenuIds) ? false : true;
    }

    private LoginInfoDto getLoginInfoDto(HttpServletRequest request) {
        LoginInfoDto loginInfos = new LoginInfoDto();
        loginInfos.setUserId(isNullOrEmpty(request.getHeader("userId")) ? null : Long.parseLong(request.getHeader("userId")));
        loginInfos.setAppId(request.getHeader("appId"));
        loginInfos.setOwnerCode(request.getHeader("ownerCode"));
        loginInfos.setEmpId(isNullOrEmpty(request.getHeader("empId")) ? null : Long.parseLong(request.getHeader("empId")));
        loginInfos.setOwnerId(request.getHeader("ownerId"));
        loginInfos.setUserCode(request.getHeader("userCode"));
        loginInfos.setLoginWay(request.getHeader("loginWay"));
        loginInfos.setCompanyId(isNullOrEmpty(request.getHeader("companyId")) ? null : Long.parseLong(request.getHeader("companyId")));
        loginInfos.setDataType(isNullOrEmpty(request.getHeader("dataType")) ? null : Long.parseLong(request.getHeader("dataType")));
        loginInfos.setDealerName(request.getHeader("dealerName"));
        loginInfos.setCompanyCode(request.getHeader("companyCode"));
        loginInfos.setOrgId(isNullOrEmpty(request.getHeader("orgId")) ? null : Long.parseLong(request.getHeader("orgId")));
        loginInfos.setOrgIds(request.getHeader("orgIds"));
        loginInfos.setEmployeeId(isNullOrEmpty(request.getHeader("employeeId")) ? null : Long.parseLong(request.getHeader("employeeId")));
        loginInfos.setUserName(request.getHeader("userName"));
        loginInfos.setGroupCode(request.getHeader("groupCode"));
        loginInfos.setEmployeeNo(request.getHeader("employeeNo"));
        loginInfos.setUserOrgId(isNullOrEmpty(request.getHeader("userOrgId")) ? null : Long.parseLong(request.getHeader("userOrgId")));
        loginInfos.setOrgName(request.getHeader("orgName"));
        loginInfos.setOrgType(isNullOrEmpty(request.getHeader("orgType")) ? null : Long.parseLong(request.getHeader("orgType")));
        return loginInfos;
    }

    private boolean isNullOrEmpty(String str) {
        if (StringUtils.isNullOrEmpty(str)) {
            return true;
        } else {
            return "null".equals(str);
        }
    }

    public String getRole(String roleId) {
        Object role = getFromRedis(BaseDataCommonConstants.USER_CACHE_ROLE, roleId);
        if (null != role) {
            return role.toString();
        } else {
            return sysmanageDataCommonClient.getRole(roleId).getData();
        }
    }

    private Object getFromRedis(String messageFormat, String Id) {
        Object object = redisClient.hget(messageFormat, Id);
        return object;
    }

    /**
     * 取得LoginInfoDTO
     * 1.首先从redis里面获取
     * 1.1 主系统直接根据userId获取
     * 1.2 子系统通过appId+userId获取
     * 2.若取得到则直接返回
     * 3.若取不到，则：
     * 3.1 主系统从数据库中读取
     * 3.2 子系统直接返回null
     *
     * @param userAgent
     * @param flag
     * @param appId
     * @return
     * @auth maxingjian
     * @since 20191129
     */
    private LoginInfoDto getLoginInfo(String userAgent) {
        String userId = "1503";
        String appId="RL";
        boolean flag=true;
        String messageFormat = MessageFormat.format(BaseDataCommonConstants.LOGIN_CACHE_USERID, userAgent);
        if (flag) {
            userId = appId + "-" + userId;
        }

        Object loginInfoDto = getFromRedis(messageFormat, userId);
        String role = JSONUtil.objectToJson(getFromRedis(BaseDataCommonConstants.USER_CACHE_ROLE, userId));
        if (null != loginInfoDto) {
            LoginInfoDto dto=new LoginInfoDto();
            dto.setAppId("RL");
            dto.setUserId(1503L);
            dto.setEmpId(1503L);
            dto.setOwnerCode("yangjie");
            dto.setCompanyId(1L);
            dto.setDataType(10461003L);
            dto.setEmployeeNo("RL2190");
            //   CMS 修改 先创建的账号 还没给权限就登录了一次 然后才分配的权限，再登录报错的问题   开始
//            if(StringUtils.isNullOrEmpty(dto.getUserOrgId())) {
//            	if (!flag) {
//                    return sysmanageDataCommonClient.getLoginInfo(userId, userAgent, flag).getData();
//                }
//                return null;
//            }
            //   CMS 修改 先创建的账号 还没给权限就登录了一次 然后才分配的权限，再登录报错的问题   结束
            if (role != null) {
                dto.setAppRole(Long.parseLong(role));
                logger.debug("============----------重新获取role----------===========");
            }
            return dto;
        } else {
            if (!flag) {
                return sysmanageDataCommonClient.getLoginInfo(userId, userAgent, flag).getData();
            }
            return null;
        }
    }

    /**
     * 路由过滤
     * 如果是需要过滤的直接返回
     */
    private boolean isIntercept(String path) {
        if (path.endsWith("/login") || path.endsWith("/login/initUserData") || path.endsWith("/login/code")
                || path.endsWith("/dealers") || path.indexOf("/login/appVersionNo") != -1) {
            return true;
        }
        //消费消息
        if (path.indexOf("/consumeMes") != -1 || path.indexOf("/tmtask") != -1 || path.indexOf("/interf") != -1) {
            return true;
        }

        //会计平台测试
        if (path.indexOf("/documentXmlGen") != -1) {
            return true;
        }
        /**
         * 系统订单
         */
        if (path.indexOf("/api/systemOrder") != -1) {
            return true;
        }
        /**
         * swagger
         */
        if (path.indexOf("/swagger-resources") != -1) {
            logger.debug("-------swagger intercept--------");
            return true;
        }
        /**
         * 子系统认证接口
         */
        if (path.indexOf("/user/userInfo/token") != -1) {
            logger.debug("-------subSystem auth intercept--------");
            return true;
        }
        /**
         * 子系统认证接口
         */
        if (path.indexOf("/userAuth/user") != -1) {
            logger.debug("-------subSystem auth intercept--------");
            return true;
        }
        return false;
    }


    /**
     * @description：校验版本信息
     * @author ：quankk
     * @date ：Created in 2019/3/5 17:08
     * @modified By：
     */
    private Map<String, String> checkAppVersion(HttpServletRequest request) {
        String appVersion = request.getHeader("appVersion");
        String appType = request.getHeader("appType");
        String appId = request.getHeader("appId");
        if (StringUtils.isNullOrEmpty(appVersion) || StringUtils.isNullOrEmpty(appType)) {
            return null;
        }
        Map<String, String> mapVersion = appVersionNo(appType, appId);
        String appVersionNo = mapVersion.get("versionNo");
        logger.debug("into checkAppVersion:当前版本:{}，传入appType:{}，传入appVersion:{}", appVersionNo, appType, appVersion);
        if (appVersionNo.equals(appVersion)) {
            return null;
        } else {
            //校验Android和IOS
            String[] appVersionNoArray = appVersionNo.split("\\.");
            String[] appVersionArray = appVersion.split("\\.");
            for (int i = 0; i < appVersionNoArray.length; i++) {
                if (Integer.valueOf(appVersionNoArray[i]) > Integer.valueOf(appVersionArray[i])) {
                    logger.debug("checkAppVersion:版本不一致{}", mapVersion);
                    return mapVersion;
                } else if (Integer.valueOf(appVersionNoArray[i]) < Integer.valueOf(appVersionArray[i])) {
                    logger.debug("checkAppVersion:版本一致，无需继续校验");
                    return null;
                } else if (Integer.valueOf(appVersionNoArray[i]).equals(Integer.valueOf(appVersionArray[i]))) {
                    continue;
                }
            }
        }
        return null;
    }

    private Map<String, String> appVersionNo(String appType, String appId) {
        if (StringUtils.isNullOrEmpty(appId)) {
            appId = "";
        }
        Object appVersionDto = getFromRedis(BaseDataCommonConstants.VERSION_CACHE_TYPE, appType + appId);
        if (null != appVersionDto) {
            AppVersionDto appVersionDtoQ = JSONUtil.jsonToObj(JSONUtil.objectToJson(appVersionDto), AppVersionDto.class);
            Map map = new HashMap();
            map.put("versionNo", appVersionDtoQ.getVersionNo());
            map.put("remark", appVersionDtoQ.getRemark());
            String url = null;
            try {
                url = URLDecoder.decode(appVersionDtoQ.getUrl(), "utf-8");
            } catch(Exception e)
            {
                logger.debug("url为空");
            }
            map.put("url", url);
            map.put("appId", appVersionDtoQ.getAppId());
            return map;
        } else {
            return sysmanageDataCommonClient.appVersionNo(appType, appId).getData();
        }
    }
    private boolean initDmsLoginInfoDto(String authorization){
        Object loginInfoStr = redisClient.get(MessageFormat.format("dms:user:login:{0}",authorization.substring(7)));
        LoginInfoDto loginInfoDto =StringUtils.isNullOrEmpty(loginInfoStr)?null:JSONUtil.jsonToObj(loginInfoStr.toString(),LoginInfoDto.class);

        if (loginInfoDto == null ) {
            return false;
        } else {
            LoginInfoDto loginInfo = ApplicationContextHelper.getBeanByType(LoginInfoDto.class);
            ClientInfoDTO clientInfoDTO = ApplicationContextHelper.getBeanByType(ClientInfoDTO.class);
            BeanMapperUtil.copyProperties(loginInfoDto, loginInfo);
            BeanMapperUtil.copyProperties(loginInfoDto, clientInfoDTO);
        }

        return true;
    }

}
